{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Object Model"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`bqplot` is based on **Grammar of Graphics** paradigm. The *Object Model* in `bqplot` gives the user the full flexibility to build custom plots. This means the API is verbose but fully customizable.\n",
    "\n",
    "The following are the steps to build a `Figure` in `bqplot` using the Object Model:\n",
    "\n",
    "* Build the scales for `x` and `y` quantities using the `Scale` classes (Scales map the data into pixels in the figure)\n",
    "* Build the marks using the `Mark` classes. Marks represent the core plotting objects (lines, scatter, bars, pies etc.). Marks take the scale objects created in step 1 as arguments\n",
    "* Build the `axes` for `x` and `y` scales\n",
    "* Finally create a figure using `Figure` class. `Figure` takes marks and axes as inputs. `Figure` object is a widget (it inherits from `DOMWidget`) and can be rendered like any other jupyter widget\n",
    "\n",
    "Let's look a simple example to understand these concepts:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from bqplot import (LinearScale, Axis, Figure, OrdinalScale, \n",
    "                    LinearScale, Bars, Lines, Scatter)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# first, let's create two vectors x and y to plot using a Lines mark\n",
    "import numpy as np\n",
    "x = np.linspace(-10, 10, 100)\n",
    "y = np.sin(x)\n",
    "\n",
    "# 1. Create the scales\n",
    "xs = LinearScale()\n",
    "ys = LinearScale()\n",
    "\n",
    "# 2. Create the axes for x and y\n",
    "xax = Axis(scale=xs, label='X')\n",
    "yax = Axis(scale=ys, orientation='vertical', label='Y')\n",
    "\n",
    "# 3. Create a Lines mark by passing in the scales\n",
    "# note that Lines object is stored in `line` which can be used later to update the plot\n",
    "line = Lines(x=x, y=y, scales={'x': xs, 'y': ys})\n",
    "\n",
    "# 4. Create a Figure object by assembling marks and axes\n",
    "fig = Figure(marks=[line], axes=[xax, yax], title='Simple Line Chart')\n",
    "\n",
    "# 5. Render the figure using display or just as is\n",
    "fig"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For creating other marks (like scatter, pie, bars, etc.), only step 3 needs to be changed. Lets look a simple example to create a bar chart:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# first, let's create two vectors x and y to plot a bar chart\n",
    "x = list('ABCDE')\n",
    "y = np.random.rand(5)\n",
    "\n",
    "# 1. Create the scales\n",
    "xs = OrdinalScale() # note the use of ordinal scale to represent categorical data\n",
    "ys = LinearScale()\n",
    "\n",
    "# 2. Create the axes for x and y\n",
    "xax = Axis(scale=xs, label='X', grid_lines='none') # no grid lines needed for x\n",
    "yax = Axis(scale=ys, orientation='vertical', label='Y', tick_format='.0%') # note the use of tick_format to format ticks\n",
    "\n",
    "# 3. Create a Bars mark by passing in the scales\n",
    "# note that Bars object is stored in `bar` object which can be used later to update the plot\n",
    "bar = Bars(x=x, y=y, scales={'x': xs, 'y': ys}, padding=.2)\n",
    "\n",
    "# 4. Create a Figure object by assembling marks and axes\n",
    "Figure(marks=[bar], axes=[xax, yax], title='Simple Bar Chart')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Mutiple marks can be rendered in a figure. It's as easy as passing a list of marks when constructing the `Figure` object"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# first, let's create two vectors x and y\n",
    "import numpy as np\n",
    "x = np.linspace(-10, 10, 25)\n",
    "y = 3 * x + 5\n",
    "y_noise = y + 10 * np.random.randn(25) # add some random noise to y\n",
    "\n",
    "# 1. Create the scales\n",
    "xs = LinearScale()\n",
    "ys = LinearScale()\n",
    "\n",
    "# 2. Create the axes for x and y\n",
    "xax = Axis(scale=xs, label='X')\n",
    "yax = Axis(scale=ys, orientation='vertical', label='Y')\n",
    "\n",
    "# 3. Create a Lines and Scatter marks by passing in the scales\n",
    "# additional attributes (stroke_width, colors etc.) can be passed as attributes to the mark objects as needed\n",
    "line = Lines(x=x, y=y, scales={'x': xs, 'y': ys}, colors=['green'], stroke_width=3)\n",
    "scatter = Scatter(x=x, y=y_noise, scales={'x': xs, 'y': ys}, colors=['red'], stroke='black')\n",
    "\n",
    "# 4. Create a Figure object by assembling marks and axes\n",
    "# pass both the marks (line and scatter) as a list to the marks attribute\n",
    "Figure(marks=[line, scatter], axes=[xax, yax], title='Scatter and Line')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This introduction should be sufficient to get started with the *Object Model*. For detailed examples of rendering individual marks (with all their attributes) please look at the *Object Model* [examples](../Marks/Object Model)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
